// Go作者版权所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package aes

import (
	"crypto/cipher"
	"crypto/internal/subtle"
	"encoding/binary"
)

// 断言aesCipherAsm实现了ctrAble接口。
var _ ctrAble = (*aesCipherAsm)(nil)

// xorBytes对a和b的内容进行xOR运算，并将结果值放入
// dst。如果a和b的长度不同，则处理的字节数
// 将等于两者中较短者的长度。返回数字
// 处理的字节数。
// 围棋：noescape
func xorBytes(dst, a, b []byte) int

// streamBufferSize是要缓存的加密计数器值的字节数。
const streamBufferSize = 32 * BlockSize

type aesctr struct {
	block   *aesCipherAsm          // 分组密码
	ctr     [2]uint64              // 计数器的下一个值（大端）
	buffer  []byte                 // 用于加密计数器值的缓冲区
	storage [streamBufferSize]byte // 阵列备份缓冲片
}

// NewCTR返回使用AES块加密/解密的流
// 计数器模式下的密码。iv的长度必须与BlockSize相同。
func (c *aesCipherAsm) NewCTR(iv []byte) cipher.Stream {
	if len(iv) != BlockSize {
		panic("cipher.NewCTR: IV length must equal block size")
	}
	var ac aesctr
	ac.block = c
	ac.ctr[0] = binary.BigEndian.Uint64(iv[0:]) // 高位
	ac.ctr[1] = binary.BigEndian.Uint64(iv[8:]) // 低位
	ac.buffer = ac.storage[:0]
	return &ac
}

func (c *aesctr) refill() {
	// 用递增的计数填充缓冲区。
	c.buffer = c.storage[:streamBufferSize]
	c0, c1 := c.ctr[0], c.ctr[1]
	for i := 0; i < streamBufferSize; i += 16 {
		binary.BigEndian.PutUint64(c.buffer[i+0:], c0)
		binary.BigEndian.PutUint64(c.buffer[i+8:], c1)

		// 大端增量：c0高，c1低。
		c1++
		if c1 == 0 {
			// 加进位
			c0++
		}
	}
	c.ctr[0], c.ctr[1] = c0, c1
	// 在ECB模式下使用AES加密缓冲区。
	cryptBlocks(c.block.function, &c.block.key[0], &c.buffer[0], &c.buffer[0], streamBufferSize)
}

func (c *aesctr) XORKeyStream(dst, src []byte) {
	if len(dst) < len(src) {
		panic("crypto/cipher: output smaller than input")
	}
	if subtle.InexactOverlap(dst[:len(src)], src) {
		panic("crypto/cipher: invalid buffer overlap")
	}
	for len(src) > 0 {
		if len(c.buffer) == 0 {
			c.refill()
		}
		n := xorBytes(dst, src, c.buffer)
		c.buffer = c.buffer[n:]
		src = src[n:]
		dst = dst[n:]
	}
}
